#include "vm/frame.h"
#include "vm/swap.h"
#include "vm/page.h"

#include <list.h>
#include <stdio.h>
#include "threads/palloc.h"
#include "threads/thread.h"
#include "devices/block.h"
#include "userprog/pagedir.h"
#include "threads/vaddr.h"

#define MAX_SWAP 8192

static bool swap_status[MAX_SWAP];
//static struct lock block_lock;
static struct semaphore mutex;

// Initialize array of swap file
void swap_init(void) {
	int i;
	for(i = 0; i < MAX_SWAP; i++)
		swap_status[i] = false;

//	lock_init(&block_lock);
	sema_init(&mutex, 1);
}

// Find the corresponding swap file from thread_current()
struct list_elem *swap_find (void *upage) {
    struct thread* cur = thread_current();
    struct slot* sp;
    struct list_elem *e;
//    lock_acquire(&cur->swap_list_lock);
    sema_down(&mutex);

    for (e = list_begin (&cur->swapt); e != list_end (&cur->swapt); e = list_next (e)) {
		sp = (struct slot *)list_entry (e, struct slot, elem);
		if(upage == sp->upage){
//			lock_release (&cur->swap_list_lock);
			sema_up(&mutex);
			return e;
		}
	}

    sema_up(&mutex);
//    lock_release(&cur->swap_list_lock);
    return 0;
}

// Remove page
void* evict(void *upage,struct thread* newown)
{
//	lock_acquire(&block_lock);
	printf("EVIT BEFORE\n");
	sema_down(&mutex);

	struct frame* fp = LRU();


	struct slot* sp;
	struct block *swapb = block_get_role(BLOCK_SWAP);
    struct thread* owner = fp->owner;
    struct list_elem *e;
    void* buf = fp->paddr;
    struct spt_elem* spte;
    int i;
    block_sector_t tar;

    for(i = 0; i < MAX_SWAP; i += 8) {
		if(swap_status[i] == false) {
			tar = i;
			swap_status[i] = true;
			break;
		}
    }
	sp=(struct slot *) malloc (sizeof(struct slot));
	sp->pid = owner->tid;
	sp->slid = tar/8;
	sp->upage = fp->upage;

	list_push_back(&thread_current()->swapt, &sp->elem);

	pagedir_clear_page (owner->pagedir, pg_round_down(sp->upage));


	lock_acquire(&owner->spt_list_lock);
    for (e = list_begin (&owner->spt); e != list_end (&owner->spt); e = list_next (e)) {
		spte = (struct spt_elem *)list_entry (e, struct spt_elem, elem);
		if(spte->upage == fp->upage) {
			list_remove(e);
			break;
		}
	}
    lock_release(&owner->spt_list_lock);

	for(i = 0; i < 8; i++) {
		block_write (swapb,tar,buf);
		swap_status[tar] = true;
		tar++;
		buf = buf + 512;
	}


	fp->upage = upage;
	fp->owner = newown;
	fp->recent = 0;

	sema_up(&mutex);
	printf("EVIT AFTER\n");
	return fp->paddr;
}

bool readback(void* upage,void* fp)
{
//	lock_acquire(&block_lock);
	printf("READBACK BEGIN\n");
	sema_down(&mutex);

	struct thread* cur = thread_current();
	struct slot* sp;
	struct block *swapb = block_get_role(BLOCK_SWAP);
	struct list_elem *e;
	block_sector_t tar;
	void* buf;
	int i;

	lock_acquire(&cur->swap_list_lock);
	for (e = list_begin (&cur->swapt); e != list_end (&cur->swapt); e = list_next (e))
	{
		sp = list_entry (e, struct slot, elem);
		if(sp->pid == cur->tid)
		{
			if(sp->upage == upage) {
				tar = sp->slid*8;
				buf = fp;
				// Write back
				for(i = 0; i < 8; i++)
				{
						block_read(swapb, tar, buf);
						swap_status[tar] = false;
						tar++;
						buf = buf+512;
				}
				// Enable Mapping
				pagedir_set_page(cur->pagedir, upage, fp, true);
				// Delete Entry
				list_remove(&sp->elem);
				lock_release(&cur->swap_list_lock);
				return true;
			}
		}
	}
	lock_release (&cur->swap_list_lock);

//	lock_release(&block_lock);
	sema_up(&mutex);
	printf("READBACK END\n");
	return false;
}
